﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using XYS.Core.Models;
using XYS.Core.Service.APILoggerService;
using XYS.Core.Util;

namespace XYS.Core.Api.Middlewares
{
    /// <summary>
    /// 处理API请求日志
    /// </summary>
    public class APILoggerHandle : IHttpStreamHandle
    {
        /// <summary>
        /// 优先级，数字越大 越早处理
        /// 小于0 则不处理
        /// </summary>
        public int PriorityLevel => 10;

        /// <summary>
        /// 开始执行
        /// </summary>
        /// <param name="scope"></param>
        /// <param name="httpContext"></param>
        /// <param name="requestBody"></param>
        /// <param name="newResqustBody">新的Resqust.Body数据</param>
        /// <returns>invokeResult:执行过程数据（将会传递至Nexted方法参数内）</returns>
        public object Invoke(IServiceScope scope, HttpContext httpContext, string requestBody, out string newResqustBody)
        {
            newResqustBody = requestBody;
            if (GlobalCoreSetting.APIRequestLogSetting.UrlBlackList.Contains(httpContext.Request.Path)) return null;

            //当前登录的用户信息
            var currentUser = scope.ServiceProvider.GetRequiredService<CurrentUserInfo>();

            //获取Ip 地址
            string realIpAddress = null;
            if (httpContext.Request.Headers.ContainsKey("X-Real-IP"))
                realIpAddress = httpContext.Request.Headers["X-Real-IP"].ToString();
            if (string.IsNullOrWhiteSpace(realIpAddress))
                realIpAddress = httpContext.Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();

            //转发地址
            string forwardedIpAddress = null;
            if (httpContext.Request.Headers.ContainsKey("X-Forwarded-For"))
                forwardedIpAddress = httpContext.Request.Headers["X-Forwarded-For"].ToString();
            if (string.IsNullOrWhiteSpace(forwardedIpAddress))
                forwardedIpAddress = httpContext.Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();

            //创建日志对象
            var apiLogInfo = new APILoggerInfo()
            {
                Id = Guid.NewGuid(),
                RequestHeaders = httpContext.Request.Headers?.Select(x => new KeyValueDTO<string, string>()
                {
                    Key = x.Key,
                    Value = (string)x.Value
                }).ToList(),
                Method = httpContext.Request.Method,
                ApiHost = httpContext.Request.Host.Value,
                ApiPort = httpContext.Request.Host.Port,
                ApiPath = httpContext.Request.Path,
                QueryString = httpContext.Request.QueryString.Value,
                APIUrl = $"{httpContext.Request.Host.Value}{httpContext.Request.Path}{httpContext.Request.QueryString}",
                EnumEnvironmentType = GlobalCoreSetting.EnvironmentName,
                BeginTime = DateTime.Now,
                ContentType = httpContext.Request.ContentType,
                RequestBody = requestBody,
                OrgId = currentUser.OrgId,
                AccountId = currentUser.AccountId,
                UserName = currentUser.UserName,
                Phone = currentUser.Phone,
                Email = currentUser.Email,
                NickName = currentUser.NickName,
                Token = currentUser.Token,
                IsFake = httpContext.Request.Headers.ContainsKey("fake"),

                RealIpAddress = realIpAddress,
                ForwardedIpAddress = forwardedIpAddress,
            };
            if (httpContext.Request.ContentType != null)
            {
                if (httpContext.Request.ContentType.ToLower().StartsWith("multipart/form-data;"))
                {
                    const string contentDisposition = "Content-Disposition";
                    if (requestBody.Contains(contentDisposition))
                    {
                        int contentDispositionIndex = requestBody.IndexOf(contentDisposition);
                        if (contentDispositionIndex > 0)
                        {
                            int contentDispositionReturnIndex = requestBody.IndexOf("\r\n", contentDispositionIndex);
                            if (contentDispositionReturnIndex > 0)
                            {
                                apiLogInfo.RequestBody = requestBody[contentDispositionIndex..contentDispositionReturnIndex];
                            }
                        }
                    }
                }
            }
            //设置追踪Id
            currentUser.TrackId = apiLogInfo.Id.ToString();
            return apiLogInfo;
        }

        /// <summary>
        /// Nexted
        /// </summary>
        /// <param name="scope"></param>
        /// <param name="httpContext"></param>
        /// <param name="responseBody"></param>
        /// <param name="invokeResult"></param>
        /// <returns>新的responseBody</returns>
        public string Nexted(IServiceScope scope, HttpContext httpContext, string responseBody, object invokeResult)
        {
            if (GlobalCoreSetting.APIRequestLogSetting.UrlBlackList.Contains(httpContext.Request.Path)) return responseBody;
            if (invokeResult is APILoggerInfo apiLogInfo)
            {
                //获取http响应状态
                apiLogInfo.StatusCode = (EnumStatusCode)httpContext.Response.StatusCode;
                apiLogInfo.ResponseBody = responseBody;

                //状态描述
                apiLogInfo.StatusMessage = apiLogInfo.StatusCode.GetDescription();
                apiLogInfo.EndTime = DateTime.Now;
            }
            return responseBody;
        }

        /// <summary>
        /// 流程结束
        /// </summary>
        /// <param name="scope"></param>
        /// <param name="httpContext"></param>
        /// <param name="nextedResult"></param>
        public void ProEnd(IServiceScope scope, HttpContext httpContext, object nextedResult)
        {
            if (GlobalCoreSetting.APIRequestLogSetting.UrlBlackList.Contains(httpContext.Request.Path)) return;
            if (nextedResult is APILoggerInfo apiLogInfo)
            {
                //保存日志
                IAPILoggerService apiLoggerService = scope.ServiceProvider.GetService<IAPILoggerService>();
                apiLoggerService?.AddLog(apiLogInfo);
            }
        }
    }
}
